#include "LocalScene.h"
#include "Utils/LogUtil.h"
#include "LocalEntity.h"
#include "Utils/StringUtil.h"
#include "Game/Component/Model.h"
#include "Render/RenderState.h"
#include "Render/Imgui/imgui.h"
#include "Math/Random.h"
#include "Game/GameState.h"


void LocalScene::OnInit()
{
	mCamera.SetSpeed(6.f);

	mCoordinateProcess = GetProcess<CoordinateProcess>("CoordinateProcess");

	// mGBufferProcess = GetProcess<GBufferProcess>("GBufferProcess");

	GeometryGenerator geometryGenerator;


	GeometryGenerator::MeshData lightData = geometryGenerator.CreateSphere(0.3f, 20, 20);
	LocalEntity* lightEntity = CreateEntity<LocalEntity>("light");
	lightEntity->AddComponent<Model>(lightData);
	lightEntity->AddComponent<OpaqueObject>();
	lightEntity->GetTransform().SetLocalPosition(glm::vec3(0.f, 3.7f, 0.f));
	lightEntity->GetTransform().UpdateMatrices();

	lightEntity->mMaterial.albedo = glm::vec4(0.f, 0.75f, 0.1f, 1.f);


	GeometryGenerator::MeshData leftPanelData = geometryGenerator.CreatePanel(4.f, 6.f, glm::vec3(-1.f, 0.f, 0.f));

	LocalEntity* leftPanelEntity = CreateEntity<LocalEntity>("leftPanel");
	leftPanelEntity->AddComponent<Model>(leftPanelData);
	leftPanelEntity->AddComponent<OpaqueObject>();
	leftPanelEntity->GetTransform().SetLocalPosition(glm::vec3(4.f, 2.f, 0.f));
	leftPanelEntity->GetTransform().UpdateMatrices();

	leftPanelEntity->mMaterial.albedo = glm::vec4(0.65f, 0.05f, 0.05f, 1.f);


	GeometryGenerator::MeshData rightPanelData = geometryGenerator.CreatePanel(4.f, 6.f, glm::vec3(1.f, 0.f, 0.f));

	LocalEntity* rightPanelEntity = CreateEntity<LocalEntity>("rightPanel");
	rightPanelEntity->AddComponent<Model>(rightPanelData);
	rightPanelEntity->AddComponent<OpaqueObject>();
	rightPanelEntity->GetTransform().SetLocalPosition(glm::vec3(-4.f, 2.f, 0.f));
	rightPanelEntity->GetTransform().UpdateMatrices();

	rightPanelEntity->mMaterial.albedo = glm::vec4(0.12f, 0.45f, 0.15f, 1.f);


	GeometryGenerator::MeshData topPanelData = geometryGenerator.CreatePanel(8.f, 6.f, glm::vec3(0.f, -1.f, 0.f));

	LocalEntity* topPanelEntity = CreateEntity<LocalEntity>("topPanel");
	topPanelEntity->AddComponent<Model>(topPanelData);
	topPanelEntity->AddComponent<OpaqueObject>();
	topPanelEntity->GetTransform().SetLocalPosition(glm::vec3(0.f, 4.f, 0.f));
	topPanelEntity->GetTransform().UpdateMatrices();

	topPanelEntity->mMaterial.albedo = glm::vec4(0.72f, 0.84f, 0.96f, 1.f);


	GeometryGenerator::MeshData bottomPanelData = geometryGenerator.CreatePanel(8.f, 6.f, glm::vec3(0.f, 1.f, 0.f));

	LocalEntity* bottomPanelEntity = CreateEntity<LocalEntity>("bottomPanel");
	bottomPanelEntity->AddComponent<Model>(bottomPanelData);
	bottomPanelEntity->AddComponent<OpaqueObject>();
	bottomPanelEntity->GetTransform().SetLocalPosition(glm::vec3(0.f, 0.f, 0.f));
	bottomPanelEntity->GetTransform().UpdateMatrices();

	bottomPanelEntity->mMaterial.albedo = glm::vec4(0.90f, 0.72f, 0.88f, 1.f);


	GeometryGenerator::MeshData backPanelData = geometryGenerator.CreatePanel(8.f, 4.f, glm::vec3(0.f, 0.f, 1.f));

	LocalEntity* backPanelEntity = CreateEntity<LocalEntity>("backPanel");
	backPanelEntity->AddComponent<Model>(backPanelData);
	backPanelEntity->AddComponent<OpaqueObject>();
	backPanelEntity->GetTransform().SetLocalPosition(glm::vec3(0.f, 2.f, -3.f));
	backPanelEntity->GetTransform().UpdateMatrices();

	backPanelEntity->mMaterial.albedo = glm::vec4(0.85f, 0.85f, 0.f, 1.f);

	LocalEntity* CoffeeTableEntity = CreateEntity<LocalEntity>("CoffeeTable");
	CoffeeTableEntity->AddComponent<Model>("CoffeeTable.obj");
	CoffeeTableEntity->AddComponent<OpaqueObject>();
	CoffeeTableEntity->GetTransform().SetLocalPosition(glm::vec3(0.f, 0.f, 1.6f));
	CoffeeTableEntity->GetTransform().SetLocalScale(glm::vec3(0.5f));
	CoffeeTableEntity->GetTransform().UpdateMatrices();

	CoffeeTableEntity->mMaterial.albedo = glm::vec4(1.0f, 0.765557f, 0.f, 1.f);


	LocalEntity* LampEntity = CreateEntity<LocalEntity>("Lamp");
	LampEntity->AddComponent<Model>("Lamp.obj");
	LampEntity->AddComponent<OpaqueObject>();
	LampEntity->GetTransform().SetLocalPosition(glm::vec3(2.f, 0.f, -1.6f));
	LampEntity->GetTransform().SetLocalScale(glm::vec3(0.5f));
	LampEntity->GetTransform().UpdateMatrices();

	LampEntity->mMaterial.albedo = glm::vec4(1.0f, 0.f, 0.f, 1.f);


	LocalEntity* CouchEntity = CreateEntity<LocalEntity>("Couch");
	CouchEntity->AddComponent<Model>("Couch.obj");
	CouchEntity->AddComponent<OpaqueObject>();
	CouchEntity->GetTransform().SetLocalPosition(glm::vec3(0.f, 0.f, -1.8f));
	CouchEntity->GetTransform().SetLocalScale(glm::vec3(0.5f));
	CouchEntity->GetTransform().UpdateMatrices();

	CouchEntity->mMaterial.albedo = glm::vec4(0.f, 0.765557f, 0.336057f, 1.f);


	LocalEntity* TeapotEntity = CreateEntity<LocalEntity>("Teapot");
	TeapotEntity->AddComponent<Model>("Teapot.obj");
	TeapotEntity->AddComponent<OpaqueObject>();
	TeapotEntity->GetTransform().SetLocalPosition(glm::vec3(0.2f, 0.84f, 0.2f));
	TeapotEntity->GetTransform().SetLocalScale(glm::vec3(0.1f));
	TeapotEntity->GetTransform().UpdateMatrices();

	TeapotEntity->mMaterial.albedo = glm::vec4(1.0f, 0.23f, 0.336057f, 1.f);


	LocalEntity* VenusEntity = CreateEntity<LocalEntity>("Venus");
	VenusEntity->AddComponent<Model>("Venus.obj");
	VenusEntity->AddComponent<OpaqueObject>();
	VenusEntity->GetTransform().SetLocalPosition(glm::vec3(-3.4f, 1.7f, 2.f));
	VenusEntity->GetTransform().SetLocalScale(glm::vec3(0.3f));
	VenusEntity->GetTransform().UpdateMatrices();

	VenusEntity->mMaterial.albedo = glm::vec4(1.0f, 0.8f, 0.f, 1.f);


	LocalEntity* SofaEntity = CreateEntity<LocalEntity>("Sofa");
	SofaEntity->AddComponent<Model>("Sofa.obj");
	SofaEntity->AddComponent<OpaqueObject>();
	SofaEntity->GetTransform().SetLocalPosition(glm::vec3(1.6f, -0.15f, -2.8f));
	SofaEntity->GetTransform().SetLocalScale(glm::vec3(1.4f));
	SofaEntity->GetTransform().UpdateMatrices();

	SofaEntity->mMaterial.albedo = glm::vec4(1.0f, 0.765557f, 0.336057f, 1.f);


	LocalEntity* AudioEntity = CreateEntity<LocalEntity>("Audio");
	AudioEntity->AddComponent<Model>("Audio.obj");
	AudioEntity->AddComponent<OpaqueObject>();
	AudioEntity->GetTransform().SetLocalPosition(glm::vec3(-2.8f, -1.f, -0.8f));
	AudioEntity->GetTransform().SetLocalScale(glm::vec3(2.f));
	AudioEntity->GetTransform().SetLocalOrientation(glm::quat(glm::vec3(0.f, glm::radians(-90.f), 0.f)));
	AudioEntity->GetTransform().UpdateMatrices();

	AudioEntity->mMaterial.albedo = glm::vec4(0.5f, 0.765557f, 0.336057f, 1.f);


	LocalEntity* Audio1Entity = CreateEntity<LocalEntity>("Audio1");
	Audio1Entity->AddComponent<Model>("Audio.obj");
	Audio1Entity->AddComponent<OpaqueObject>();
	Audio1Entity->GetTransform().SetLocalPosition(glm::vec3(-3.5f, -1.f, -0.8f));
	Audio1Entity->GetTransform().SetLocalScale(glm::vec3(2.f));
	Audio1Entity->GetTransform().SetLocalOrientation(glm::quat(glm::vec3(0.f, glm::radians(-90.f), 0.f)));
	Audio1Entity->GetTransform().UpdateMatrices();

	Audio1Entity->mMaterial.albedo = glm::vec4(0.5f, 0.765557f, 0.336057f, 1.f);


	LocalEntity* Audio2Entity = CreateEntity<LocalEntity>("Audio2");
	Audio2Entity->AddComponent<Model>("Audio.obj");
	Audio2Entity->AddComponent<OpaqueObject>();
	Audio2Entity->GetTransform().SetLocalPosition(glm::vec3(-4.2f, -1.f, -0.8f));
	Audio2Entity->GetTransform().SetLocalScale(glm::vec3(2.f));
	Audio2Entity->GetTransform().SetLocalOrientation(glm::quat(glm::vec3(0.f, glm::radians(-90.f), 0.f)));
	Audio2Entity->GetTransform().UpdateMatrices();

	Audio2Entity->mMaterial.albedo = glm::vec4(0.5f, 0.765557f, 0.336057f, 1.f);


	LocalEntity* TableEntity = CreateEntity<LocalEntity>("Table");
	TableEntity->AddComponent<Model>("Table.obj");
	TableEntity->AddComponent<OpaqueObject>();
	TableEntity->GetTransform().SetLocalPosition(glm::vec3(0.f, 0.f, 0.f));
	TableEntity->GetTransform().SetLocalScale(glm::vec3(0.02f, 0.02f, 0.011f));
	TableEntity->GetTransform().SetLocalOrientation(glm::quat(glm::vec3(0.f, glm::radians(90.f), glm::radians(90.f))));
	TableEntity->GetTransform().UpdateMatrices();

	TableEntity->mMaterial.albedo = glm::vec4(1.0f, 0.765557f, 0.336057f, 1.f);



	LocalEntity* TVEntity = CreateEntity<LocalEntity>("TV");
	TVEntity->AddComponent<Model>("TV.obj");
	TVEntity->AddComponent<OpaqueObject>();
	TVEntity->GetTransform().SetLocalOrientation(glm::quat(glm::vec3(0.f, glm::radians(-90.f), 0.f)));
	TVEntity->GetTransform().SetLocalPosition(glm::vec3(-2.6f, 0.f, 0.f));
	TVEntity->GetTransform().SetLocalScale(glm::vec3(2.f));
	TVEntity->GetTransform().UpdateMatrices();

	TVEntity->mMaterial.albedo = glm::vec4(1.0f, 0.765557f, 0.336057f, 1.f);


	GeometryGenerator::MeshData TVBoardData = geometryGenerator.CreatePanel(1.46f, 2.84f, glm::vec3(-1.f, 0.f, 0.f));

	LocalEntity* TVBoardEntity = CreateEntity<LocalEntity>("TVBoard");
	TVBoardEntity->AddComponent<Model>(TVBoardData);
	TVBoardEntity->AddComponent<OpaqueObject>();
	TVBoardEntity->GetTransform().SetLocalPosition(glm::vec3(3.91f, 2.63f, 0.02f));
	TVBoardEntity->GetTransform().UpdateMatrices();

	TVBoardEntity->mMaterial.albedo = glm::vec4(0.65f, 0.01f, 0.045f, 1.f);



	LocalEntity* LionHeadEntity = CreateEntity<LocalEntity>("LionHead");
	LionHeadEntity->AddComponent<Model>("LionHead.obj");
	LionHeadEntity->AddComponent<OpaqueObject>();
	LionHeadEntity->GetTransform().SetLocalPosition(glm::vec3(-2.f, 2.f, -3.f));
	LionHeadEntity->GetTransform().SetLocalScale(glm::vec3(0.06f));
	LionHeadEntity->GetTransform().SetLocalOrientation(glm::quat(glm::vec3(glm::radians(-90.f), 0.f, 0.f)));
	LionHeadEntity->GetTransform().UpdateMatrices();

	LionHeadEntity->mMaterial.albedo = glm::vec4(0.6f, 0.2f, 0.336057f, 1.f);


	LocalEntity* DogHeadEntity = CreateEntity<LocalEntity>("DogHead");
	DogHeadEntity->AddComponent<Model>("DogHead.obj");
	DogHeadEntity->AddComponent<OpaqueObject>();
	DogHeadEntity->GetTransform().SetLocalPosition(glm::vec3(1.2f, 2.f, -3.f));
	DogHeadEntity->GetTransform().SetLocalScale(glm::vec3(0.06f));
	DogHeadEntity->GetTransform().SetLocalOrientation(glm::quat(glm::vec3(glm::radians(-90.f), 0.f, 0.f)));
	DogHeadEntity->GetTransform().UpdateMatrices();

	DogHeadEntity->mMaterial.albedo = glm::vec4(0.6f, 0.2f, 0.336057f, 1.f);


	LocalEntity* FoxHeadEntity = CreateEntity<LocalEntity>("FoxHead");
	FoxHeadEntity->AddComponent<Model>("FoxHead.obj");
	FoxHeadEntity->AddComponent<OpaqueObject>();
	FoxHeadEntity->GetTransform().SetLocalPosition(glm::vec3(-0.4f, 2.f, -3.f));
	FoxHeadEntity->GetTransform().SetLocalScale(glm::vec3(0.06f));
	FoxHeadEntity->GetTransform().SetLocalOrientation(glm::quat(glm::vec3(glm::radians(-90.f), 0.f, 0.f)));
	FoxHeadEntity->GetTransform().UpdateMatrices();

	FoxHeadEntity->mMaterial.albedo = glm::vec4(0.6f, 0.2f, 0.336057f, 1.f);


	LocalEntity* GrooveEntity = CreateEntity<LocalEntity>("Groove");
	GrooveEntity->AddComponent<Model>("Groove.obj");
	GrooveEntity->AddComponent<OpaqueObject>();
	GrooveEntity->GetTransform().SetLocalOrientation(glm::quat(glm::vec3(0.f, glm::radians(90.f), 0.f)));
	GrooveEntity->GetTransform().SetLocalPosition(glm::vec3(4.1f, 2.5f, 0.f));
	GrooveEntity->GetTransform().SetLocalScale(glm::vec3(2.f));
	GrooveEntity->GetTransform().UpdateMatrices();

	GrooveEntity->mMaterial.albedo = glm::vec4(0.4f, 0.f, 0.336057f, 1.f);


	LocalEntity* PianoEntity = CreateEntity<LocalEntity>("Piano");
	PianoEntity->AddComponent<Model>("Piano.obj");
	PianoEntity->AddComponent<OpaqueObject>();
	PianoEntity->GetTransform().SetLocalPosition(glm::vec3(1.f, 0.f, 3.4f));
	PianoEntity->GetTransform().SetLocalScale(glm::vec3(2.f));
	PianoEntity->GetTransform().UpdateMatrices();

	PianoEntity->mMaterial.albedo = glm::vec4(1.0f, 0.765557f, 0.336057f, 1.f);


	LocalEntity* CurtainEntity = CreateEntity<LocalEntity>("Curtain");
	CurtainEntity->AddComponent<Model>("Curtain.obj");
	CurtainEntity->AddComponent<OpaqueObject>();
	CurtainEntity->GetTransform().SetLocalPosition(glm::vec3(5.6f, 1.6f, -2.8f));
	CurtainEntity->GetTransform().SetLocalScale(glm::vec3(1.f));
	CurtainEntity->GetTransform().UpdateMatrices();

	CurtainEntity->mMaterial.albedo = glm::vec4(0.2f, 0.f, 0.936057f, 1.f);





#if 0
	LocalEntity* CoffeeTableEntity = CreateEntity<LocalEntity>("CoffeeTable");
	CoffeeTableEntity->AddComponent<Model>("CoffeeTable.obj");
	CoffeeTableEntity->GetTransform().SetLocalPosition(glm::vec3());
	CoffeeTableEntity->GetTransform().SetLocalScale(glm::vec3(0.3f));
	CoffeeTableEntity->GetTransform().UpdateMatrices();

	CoffeeTableEntity->mMaterial.albedo = glm::vec4(1.0f, 0.765557f, 0.336057f, 1.f);
#endif

	mGBufferIndex = 0;

	mRoughnessMultiplier = 1.f;

	for (uint32 i = 0; i < HALTON_SAMPLES; i++)
		mJitterSamples.push_back(glm::vec2((2.0f * HaltonSequence(2, i) - 1.0f), (2.0f * HaltonSequence(3, i) - 1.0f)));

	uint32 jitterIndex = GGameState->GetFrameAmount() % HALTON_SAMPLES;
	glm::vec2 halton = mJitterSamples[jitterIndex];
	mCurrentJitter = glm::vec2(halton.x / float(GRenderState->GetWidth()), halton.y / float(GRenderState->GetHeight()));
	mPrevJitter = mCurrentJitter;

	mPerFrameUbo.viewInverse = glm::inverse(mCamera.GetView());
	mPerFrameUbo.viewInverse = glm::inverse(mCamera.GetProj());
	mPerFrameUbo.projView = mCamera.GetProj() * mCamera.GetView();
	mPerFrameUbo.projViewInverse = glm::inverse(mPerFrameUbo.projView);
	mPerFrameUbo.prevProjView = mPerFrameUbo.projView;
	mPerFrameUbo.cameraPosition = glm::vec4(mCamera.GetPosition(), 1.f);
	mPerFrameUbo.currentPrevJitter = glm::vec4(mCurrentJitter, mPrevJitter);

	mOpaqueEntitys = GetEntitysByTypes<LocalEntity, Model, OpaqueObject>();

	mGBufferPushConstants.resize(mOpaqueEntitys.size(), {});

	for (uint32 i = 0; i < mOpaqueEntitys.size(); ++i)
	{
		mGBufferPushConstants[i].objectIndex = i;
		mGBufferPushConstants[i].roughnessMultiplier = mRoughnessMultiplier;
		mGBufferPushConstants[i].prevModel = mOpaqueEntitys[i]->GetTransform().GetWorldMatrix();
		mGBufferPushConstants[i].model = mOpaqueEntitys[i]->GetTransform().GetWorldMatrix();
	}


}

void LocalScene::OnTick(float deltaTime)
{
	mPrevJitter = mCurrentJitter;
	uint32 jitterIndex = GGameState->GetFrameAmount() % HALTON_SAMPLES;
	glm::vec2 halton = mJitterSamples[jitterIndex];
	mCurrentJitter = glm::vec2(halton.x / float(GRenderState->GetWidth()), halton.y / float(GRenderState->GetHeight()));

	mPerFrameUbo.viewInverse = glm::inverse(mCamera.GetView());
	mPerFrameUbo.viewInverse = glm::inverse(mCamera.GetProj());
	mPerFrameUbo.prevProjView = mPerFrameUbo.projView;
	mPerFrameUbo.projView = mCamera.GetProj() * mCamera.GetView();
	mPerFrameUbo.projViewInverse = glm::inverse(mPerFrameUbo.projView);
	mPerFrameUbo.cameraPosition = glm::vec4(mCamera.GetPosition(), 1.f);
	mPerFrameUbo.currentPrevJitter = glm::vec4(mCurrentJitter, mPrevJitter);

	for (uint32 i = 0; i < mOpaqueEntitys.size(); ++i)
	{
		mGBufferPushConstants[i].roughnessMultiplier = mRoughnessMultiplier;
		mGBufferPushConstants[i].prevModel = mGBufferPushConstants[i].model;
		mGBufferPushConstants[i].model = mOpaqueEntitys[i]->GetTransform().GetWorldMatrix();
	}
}

void LocalScene::OnImgui()
{
	if (ImGui::BeginMainMenuBar())
	{
		ImGui::Text("Fps : %d ", GRenderState->GetFps());

		ImGui::RadioButton("GBuffer 1", &mGBufferIndex, 0);
		ImGui::RadioButton("GBuffer 2", &mGBufferIndex, 1);
		ImGui::RadioButton("GBuffer 3", &mGBufferIndex, 2);

		ImGui::EndMainMenuBar();
	}

	switch (mGBufferIndex)
	{
	case 0:
		mCoordinateProcess->UpdateColorRenderTarget("GBufferProcess", "GBuffer1Texture");
		break;
	case 1:
		mCoordinateProcess->UpdateColorRenderTarget("GBufferProcess", "GBuffer2Texture");
		break;
	case 2:
		mCoordinateProcess->UpdateColorRenderTarget("GBufferProcess", "GBuffer3Texture");
		break;
	}
}

void LocalScene::OnExit()
{

}